package org.hcf.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 1．号码的结构 　　- 公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。
 * 	排列顺序从左至右依次为：
 * 		六位数字地址码，
 * 		八位数字出生日期码，
 * 		三位数字顺序码和一位数字校验码。
 *
 * 2．地址码 　　表示编码对象常住户口所在县（县级市、旗、区）的行政区划代码，按GB/T2260的规定执行。
 *
 * 3．出生日期码 　　表示编码对象出生的年、月、日，按GB/T7408的规定执行，年、月、日代码之间不用分隔符。
 *
 * 4．顺序码 　　表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配给女性。
 *
 * 5．校验码 　　根据前面十七位数字码，按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。
 *
 * 参考文献
 * https://blog.csdn.net/qiphon3650/article/details/95541641
 * https://baike.baidu.com/item/%E5%B1%85%E6%B0%91%E8%BA%AB%E4%BB%BD%E8%AF%81%E5%8F%B7%E7%A0%81/3400358?fr=aladdin https://juejin.im/post/5aa8d89af265da23866f9669
 *
 * @author changfu.he
 * @email changfu.he@weimob.com
 * @date 2021/9/22
 */
public class IDCardNoUtils {
    public static final Logger LOGGER = LoggerFactory.getLogger(IDCardNoUtils.class);

    /**
     * 地址码长6位 以数字1-9开头 后5位为0-9的数字 根据以上规则，写出地址码的正则表达式
     */
    private static final String ADDR_REG = "(?<addr>[1-9]\\d{5})";

    /**
     * 年份码长4位 支持1900-2399 年份
     */
    private static final String YEAR_REG = "(?<year>(19|2[0-3])\\d{2})";

    /**
     * 月份长2为 01- 12
     */
    private static final String MONTH_REG = "(?<month>0[1-9]|1[0-2])";

    /**
     * 日期：01-31
     */
    private static final String DATE_REG = "(?<date>0[1-9]|[1-2][0-9]|3[0-1])";

    /**
     * 顺序码: 表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配给女性
     */
    private static final String SEQUENCE_REG = "(?<sequence>\\d{3})";

    /**
     * 校验码长1位 可以是数字，字母x或字母X 根据以上规则
     */
    private static final String CHECK_REG = "[0-9Xx]";

    /**
     * 18位身份证校验
     */
    public static final String ID_CARD_REG = "^" + ADDR_REG + "(?<birthday>" + YEAR_REG + MONTH_REG + DATE_REG + ")" + SEQUENCE_REG + CHECK_REG + "$";


    public static boolean matches(String str){
        try {
            Pattern pattern = Pattern.compile(ID_CARD_REG);
            Matcher matcher = pattern.matcher(str);
            matcher.find();
//            String year = matcher.group("year");
//            String month = matcher.group("month");
//            String date = matcher.group("date");
            String birthday = matcher.group("birthday");
            return checkDate(birthday,"yyyyMMdd");
        }catch (Exception e){
            LOGGER.error("身份证校验发生异常！",e);
            return false;
        }
    }

    public static boolean checkDate(String str, String dateFmt) {
        //括号内为日期格式，y代表年份，M代表年份中的月份（为避免与小时中的分钟数m冲突，此处用M），d代表月份中的天数
        SimpleDateFormat sd = new SimpleDateFormat(dateFmt);
        try {
            //此处指定日期/时间解析是否不严格，在true是不严格，false时为严格
            sd.setLenient(false);
            //从给定字符串的开始解析文本，以生成一个日期
            sd.parse(str);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

}